/*
 * PSP Software Development Kit - http://www.psvdev.org
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 * scr_printf.c - Debug screen functions.
 *
 * Copyright (c) 2005 Marcus R. Brown <mrbrown@ocgnet.org>
 * Copyright (c) 2005 James Forshaw <tyranid@gmail.com>
 * Copyright (c) 2005 John Kelley <ps2dev@kelley.ca>
 *
 * $Id: scr_printf.c 2450 2009-01-04 23:53:02Z oopo $
 */
#include <vitasdk.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>

#include "pspdebug.h"

extern unsigned char msx_asc[];
extern int font_asc_width, font_asc_height;

static int font_chs_width = 16, font_chs_height = 18;
static int font_asc_bytes, font_chs_bytes;
static int font_width, font_height;

#define FONT_HIGHT font_asc_height
#define LINE_HIGHT (FONT_HIGHT + 4)
#define BW_FONT_PATH "sa0:data/font/pgf/gb3s1518.bwfon"
#define BW_FONT_SIZE 1023372
#define BW_FONT_N_CHARS (BW_FONT_SIZE/36)

#define SCREEN_WIDTH 960
#define SCREEN_HEIGHT 544
#define SCREEN_FB_WIDTH 960
#define SCREEN_FB_SIZE (2 * 1024 * 1024)

static int X = 0, Y = 0;
static uint32_t color_bg = 0xFF000000, color_fg = 0xFFFFFFFF;
static void *g_vram_base;
static int clearline_en = 1;

static unsigned char msx_chs[BW_FONT_SIZE];

static const unsigned short bw_charmap_compr[]={ 0x00a4, 1, 0x00a7, 2, 0x00b0, 2, 0x00b7, 1, 0x00d7, 1, 0x00e0, 2, 0x00e8, 3, 0x00ec, 2,
												0x00f2, 2, 0x00f7, 1, 0x00f9, 2, 0x00fc, 1, 0x0101, 1, 0x0113, 1, 0x011b, 1, 0x012b, 1,
												0x0144, 1, 0x0148, 1, 0x014d, 1, 0x016b, 1, 0x01ce, 1, 0x01d0, 1, 0x01d2, 1, 0x01d4, 1,
												0x01d6, 1, 0x01d8, 1, 0x01da, 1, 0x01dc, 1, 0x0251, 1, 0x0261, 1, 0x02c7, 1, 0x02c9, 3,
												0x02d9, 1, 0x0391, 17, 0x03a3, 7, 0x03b1, 17, 0x03c3, 7, 0x0401, 1, 0x0410, 64, 0x0451, 1,
												0x2010, 1, 0x2013, 4, 0x2018, 2, 0x201c, 2, 0x2025, 2, 0x2030, 1, 0x2032, 2, 0x2035, 1,
												0x203b, 1, 0x20ac, 1, 0x2103, 1, 0x2105, 1, 0x2109, 1, 0x2116, 1, 0x2121, 1, 0x2160, 12,
												0x2170, 10, 0x2190, 4, 0x2196, 4, 0x2208, 1, 0x220f, 1, 0x2211, 1, 0x2215, 1, 0x221a, 1,
												0x221d, 4, 0x2223, 1, 0x2225, 1, 0x2227, 5, 0x222e, 1, 0x2234, 4, 0x223d, 1, 0x2248, 1,
												0x224c, 1, 0x2252, 1, 0x2260, 2, 0x2264, 4, 0x226e, 2, 0x2295, 1, 0x2299, 1, 0x22a5, 1,
												0x22bf, 1, 0x2312, 1, 0x2460, 10, 0x2474, 40, 0x2500, 76, 0x2550, 36, 0x2581, 15, 0x2593, 3,
												0x25a0, 2, 0x25b2, 2, 0x25bc, 2, 0x25c6, 2, 0x25cb, 1, 0x25ce, 2, 0x25e2, 4, 0x2605, 2,
												0x2609, 1, 0x2640, 1, 0x2642, 1, 0x2e81, 1, 0x2e84, 1, 0x2e88, 1, 0x2e8b, 2, 0x2e97, 1,
												0x2ea7, 1, 0x2eaa, 1, 0x2eae, 1, 0x2eb3, 1, 0x2eb6, 2, 0x2ebb, 1, 0x2eca, 1, 0x2ff0, 12,
												0x3000, 4, 0x3005, 19, 0x301d, 2, 0x3021, 9, 0x303e, 1, 0x3041, 83, 0x309b, 4, 0x30a1, 86,
												0x30fc, 3, 0x3105, 37, 0x3220, 10, 0x3231, 1, 0x32a3, 1, 0x338e, 2, 0x339c, 3, 0x33a1, 1,
												0x33c4, 1, 0x33ce, 1, 0x33d1, 2, 0x33d5, 1, 0x3400, 6582, 0x4e00, 20902, 0xe78d, 10, 0xe7c7, 2,
												0xe816, 3, 0xe81e, 1, 0xe826, 1, 0xe82b, 2, 0xe831, 2, 0xe83b, 1, 0xe843, 1, 0xe854, 2,
												0xe864, 1, 0xf92c, 1, 0xf979, 1, 0xf995, 1, 0xf9e7, 1, 0xf9f1, 1, 0xfa0c, 4, 0xfa11, 1,
												0xfa13, 2, 0xfa18, 1, 0xfa1f, 3, 0xfa23, 2, 0xfa27, 3, 0xfe30, 2, 0xfe33, 18, 0xfe49, 10,
												0xfe54, 4, 0xfe59, 14, 0xfe68, 4, 0xff01, 94, 0xffe0, 6 };

static const int bw_charmap_compr_len = 165;

int utf8_to_ucs2(const char *utf8, unsigned int *character)
{
	if (((utf8[0] & 0xF0) == 0xE0) && ((utf8[1] & 0xC0) == 0x80) && ((utf8[2] & 0xC0) == 0x80))
	{
		*character = ((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F);
		return 3;
	}
	else if (((utf8[0] & 0xE0) == 0xC0) && ((utf8[1] & 0xC0) == 0x80))
	{
		*character = ((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F);
		return 2;
	}
	else
	{
		*character = utf8[0];
		return 1;
	}
}

int bwFontGetID(unsigned int ucs) {
	int j, id = 0;
	char found = 0;
	for (j = 0; j < bw_charmap_compr_len && !found; j++) {
		if ((ucs >= bw_charmap_compr[j*2]) && (ucs < (bw_charmap_compr[j*2] + bw_charmap_compr[j*2+1]))) {
			id += ucs - bw_charmap_compr[j*2];
			found = 1;
		}
		else {
			id += bw_charmap_compr[j*2+1];
		}
	}
	if (!found)  //char not in charmap
		return 65535;
	if (id >= BW_FONT_N_CHARS) //char not in fontdata or not in ASCII-cache
		return 65535;

	return id;
}

int bwFontReadFile() {
	int fd = sceIoOpen(BW_FONT_PATH, SCE_O_RDONLY, 0);
	sceIoRead(fd, msx_chs, BW_FONT_SIZE);
	sceIoClose(fd);

	return 0;
}

void psvDebugScreenInit(void)
{
	X = Y = 0;

	font_asc_bytes = ((int)((font_asc_width+7)/8))*font_asc_height;
	font_chs_bytes = ((int)((font_chs_width+7)/8))*font_chs_height;

	bwFontReadFile();

	int block = sceKernelAllocMemBlock("display", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, SCREEN_FB_SIZE, NULL);
	sceKernelGetMemBlockBase(block, &g_vram_base);

	SceDisplayFrameBuf framebuf;
	framebuf.size = sizeof(framebuf);
	framebuf.base = g_vram_base;
	framebuf.pitch = SCREEN_FB_WIDTH;
	framebuf.width = SCREEN_WIDTH;
	framebuf.height = SCREEN_HEIGHT;
	framebuf.pixelformat = SCE_DISPLAY_PIXELFORMAT_A8B8G8R8;
	sceDisplaySetFrameBuf(&framebuf, 1);
}

void psvDebugScreenClear(void)
{
	X = Y = 0;
	int i;
	uint32_t *vram = (uint32_t *)g_vram_base;
	for (i = 0; i < SCREEN_FB_WIDTH * SCREEN_HEIGHT; i++)
	{
		vram[i] = color_bg;
	}
}

void psvDebugScreenClearLine(int coord_y)
{
	int x = 0, y = coord_y;
	uint32_t *vram, *vram_ptr;
	vram = (uint32_t *)g_vram_base + x + y * SCREEN_FB_WIDTH;
	for (; y < LINE_HIGHT; y++, vram += SCREEN_FB_WIDTH)
	{
		vram_ptr = vram;
		for (x = 0; x < SCREEN_FB_WIDTH; x++, vram_ptr++)
		{
			*vram_ptr = color_bg;
		}
	}
}

void psvDebugScreenClearLineEnable(void)
{
	clearline_en = 1;
}

void psvDebugScreenClearLineDisable(void)
{
	clearline_en = 0;
}

void psvDebugScreenSetBackColor(uint32_t color)
{
	color_bg = color;
}

void psvDebugScreenSetTextColor(uint32_t color)
{
	color_fg = color;
}

void psvDebugScreenSetXY(int x, int y)
{
	X = x;
	Y = y;
}

int psvDebugScreenGetX(void)
{
	return X;
}

int psvDebugScreenGetY(void)
{
	return Y;
}

void psvDebugDrawLine(int width, uint32_t color)
{
	uint32_t *vram = (uint32_t *)g_vram_base + X + Y * SCREEN_FB_WIDTH;
	for (; X < width; X++, vram++)
	{
		if (X > SCREEN_WIDTH)
			break;
		*vram = color;
	}
}

int psvDebugScreenPuts(const char *msg)
{
	unsigned int character;
	unsigned char *font;
	int font_id, offset;
	uint32_t *vram, *vram_ptr;
	int i;
	for (i = 0; msg[i];)
	{
		i += utf8_to_ucs2(&msg[i], &character);
		if (character == '\n')
		{
			X = 0;
			Y += LINE_HIGHT;
			continue;
		}

		if (character >= 0 && character <= 0x7f) {
			font_width = font_asc_width;
			font_height = font_asc_height;
			offset = character*font_asc_bytes;
			font = &msx_asc[offset];
		}
		else if (character >= 0xa4 && character <= 0xffe5) {
			font_width = font_chs_width;
			font_height = font_chs_height;
			font_id = bwFontGetID(character);
			if (font_id >= 65535)
				continue;
			offset  = font_id*font_chs_bytes;
			font = &msx_chs[offset];
		}
		else
		{
			continue;
		}

		int is_new_line = 0;
		if (X + font_width > SCREEN_WIDTH)
		{
			Y += LINE_HIGHT;
			X = 0;
			is_new_line = 1;
		}
		if (Y + LINE_HIGHT > SCREEN_HEIGHT)
		{
			X = Y = 0;
			is_new_line = 1;
		}
		if (is_new_line)
		{
			if (clearline_en)
			{
				psvDebugScreenClearLine(Y);
			}
			else if (Y == 0)
			{
				psvDebugScreenClear();
			}
		}

		vram = (uint32_t *)g_vram_base + X + Y * SCREEN_FB_WIDTH;
		int p;
		for (int row = 0; row < font_height; row++, vram += SCREEN_FB_WIDTH)
		{
			p = 0;
			vram_ptr = vram;
			for (int col = 0; col < font_width; col++, p++, vram_ptr++)
			{
				if (p > 7)
				{
					font++;
					p = 0;
				}
				*vram_ptr = (*font & (0x80 >> p)) ? color_fg : color_bg;
			}
			font++;
		}
		X += font_width;
	}

	return i;
}

void psvDebugScreenPrintf(const char *format, ...)
{
	char buf[512];

	va_list opt;
	va_start(opt, format);
	sceClibVsnprintf(buf, sizeof(buf), format, opt);
	psvDebugScreenPuts(buf);
	va_end(opt);
}
